package com.cbs.android.component.graphicsutils;

import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.media.ExifInterface;
import android.os.Build;
import android.view.View;

import com.cbs.android.component.log.L;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

/**
 * Created by broche on 3/31/14.
 */
public final class GraphicsUtils {
    private static final String TAG = GraphicsUtils.class.getName();

    public static Bitmap view2Bitmap(View view) {
        Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas();
        canvas.setBitmap(bitmap);
        view.draw(canvas);
        canvas.save(Canvas.ALL_SAVE_FLAG);
        canvas.restore();
        return bitmap;
    }

    public static Bitmap loadBitmap(String path) {
        Bitmap result;
        Bitmap bitmap = BitmapFactory.decodeFile(path);
        int rotation = getExifRotation(path);
        if (rotation != 0) {
            result = rotateBitmap(bitmap, rotation);
            bitmap.recycle();
        } else {
            result = bitmap;
        }
        return result;
    }

    public static Bitmap drawBitmap(Bitmap originBitmap, Bitmap newBitmap, float left, float top) {
        Canvas canvas = new Canvas(originBitmap);
        canvas.drawBitmap(newBitmap, left, top, null);
        canvas.save(Canvas.ALL_SAVE_FLAG);
        canvas.restore();
        return originBitmap;
    }

    /**
     * clip bitmap depending on rect
     *
     * @param bitmap target bitmap
     * @param rect   rect area
     * @return bitmap if success, null if exception
     */
    public static Bitmap clipBitmap(Bitmap bitmap, Rect rect) {
        return Bitmap.createBitmap(bitmap, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
    }

    public static Bitmap zoomBitmap(Bitmap bitmap, float scaleWidth, float scaleHeight) {
        int originWidth = bitmap.getWidth();
        int originHeight = bitmap.getHeight();
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);
        return Bitmap.createBitmap(bitmap, 0, 0, originWidth, originHeight, matrix, true);
    }

    public static Bitmap rotateBitmap(Bitmap bitmap, int rotation) {
        int originWidth = bitmap.getWidth();
        int originHeight = bitmap.getHeight();
        Matrix matrix = new Matrix();
        matrix.postRotate(rotation);
        return Bitmap.createBitmap(bitmap, 0, 0, originWidth, originHeight, matrix, true);
    }

    public static byte[] compressBitmap(String filePath, int minSideLength, int quality, Bitmap.CompressFormat compressFormat) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = calculateInSampleSize(filePath, minSideLength);
        Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
        if (bitmap == null) {
            return null;
        }

        int degree = getExifRotation(filePath);
        float scale = getScale(bitmap, minSideLength);
        byte[] result = compressBitmap(bitmap, degree, scale, quality, compressFormat);

        bitmap.recycle();

        return result;
    }

    public static byte[] compressBitmap(byte[] bytes, int minSideLength, int quality, Bitmap.CompressFormat compressFormat) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = calculateInSampleSize(bytes, minSideLength);
        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
        if (bitmap == null) {
            return null;
        }

        int degree = 0;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            degree = getExifRotation(new ByteArrayInputStream(bytes));
        }
        float scale = getScale(bitmap, minSideLength);
        byte[] result = compressBitmap(bitmap, degree, scale, quality, compressFormat);

        bitmap.recycle();

        return result;
    }

    public static byte[] compressBitmap(Bitmap bitmap, int minSideLength, int quality, Bitmap.CompressFormat compressFormat) {
        int degree = 0;
        float scale = getScale(bitmap, minSideLength);
        byte[] result = compressBitmap(bitmap, degree, scale, quality, compressFormat);

        return result;
    }

    private static byte[] compressBitmap(Bitmap bitmap, int degree, float scale, int quality, Bitmap.CompressFormat compressFormat) {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        byte[] result;
        if (degree != 0 || scale < 1) {
            Matrix matrix = new Matrix();
            if (degree != 0) {
                matrix.postRotate(degree);
            }
            if (scale < 1) {
                matrix.postScale(scale, scale);
            }

            Bitmap convertedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            convertedBitmap.compress(compressFormat, quality, baos);
            result = baos.toByteArray();
            try {
                baos.close();
            } catch (IOException e) {
                L.e(TAG, "", e);
            }

            convertedBitmap.recycle();
        } else {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bitmap.compress(compressFormat, quality, baos);
            result = baos.toByteArray();
            try {
                baos.close();
            } catch (IOException e) {
                L.e(TAG, "", e);
            }
        }

        return result;
    }

    public static int calculateInSampleSize(InputStream inputStream, int minSideLength) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(inputStream, null, options);
        return calculateInSampleSize(options, minSideLength);
    }

    public static int calculateInSampleSize(byte[] bytes, int minSideLength) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
        return calculateInSampleSize(options, minSideLength);
    }

    public static int calculateInSampleSize(String filePath, int minSideLength) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);
        return calculateInSampleSize(options, minSideLength);
    }

    private static int calculateInSampleSize(BitmapFactory.Options options, int minSideLength) {
        // Raw height and width of image
        final int length = options.outHeight < options.outWidth ? options.outHeight : options.outWidth;
        int inSampleSize = 1;

        if (length > minSideLength) {
            // Calculate ratios of height and width to requested height and
            // width
            inSampleSize = Math.round((float) length / (float) minSideLength);
        }

        return inSampleSize;
    }

    public static int getExifRotation(String path) {
        int degree = 0;
        try {
            ExifInterface exifInterface = new ExifInterface(path);
            degree = getExifRotation(exifInterface);
        } catch (IOException e) {
            L.e(TAG, "Error getting Exif data", e);
        }
        return degree;
    }

    @TargetApi(Build.VERSION_CODES.N)
    public static int getExifRotation(InputStream inputStream) {
        int degree = 0;
        try {
            ExifInterface exifInterface = new ExifInterface(inputStream);
            degree = getExifRotation(exifInterface);
        } catch (IOException e) {
            L.e(TAG, "Error getting Exif data", e);
        }
        return degree;
    }

    public static int getExifRotation(ExifInterface exifInterface) {
        int degree = 0;
        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                degree = 90;
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                degree = 180;
                break;
            case ExifInterface.ORIENTATION_ROTATE_270:
                degree = 270;
                break;
        }
        return degree;
    }


    @TargetApi(Build.VERSION_CODES.ECLAIR)
    public static boolean copyExifRotation(File sourceFile, File destFile) {
        if (sourceFile == null || destFile == null) return false;
        try {
            ExifInterface exifSource = new ExifInterface(sourceFile.getAbsolutePath());
            ExifInterface exifDest = new ExifInterface(destFile.getAbsolutePath());
            exifDest.setAttribute(ExifInterface.TAG_ORIENTATION, exifSource.getAttribute(ExifInterface.TAG_ORIENTATION));
            exifDest.saveAttributes();
            return true;
        } catch (IOException e) {
            L.e(TAG, "Error copying Exif data", e);
            return false;
        }
    }

    private static float getScale(Bitmap bitmap, int minLength) {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        return minLength * 1.0f / (width < height ? width : height);
    }
}
